///	@NOTE THIS IS AN TEST APPLICATION. To compile it, remove any
/// UNIT TEST include from CoolVES_initmain.cpp. Then ensure
/// that this testapp is linked to CoolVES_initmain.cpp

#include <stdint.h>
#include <iostream>
#include <sstream>
#include <cmath>
#include <ctime>
#include <cstdlib>

#include <ObjectSubSystem/ObjectSubSystem.h>
#include <RenderSubSystem/RenderSubSystem.h>
#include <Exceptions/Exception.h>
#include <Math/Vector.hpp>
#include <Math/Matrix4x4.hpp>

float wrapdegree(float angle)
{
	if(angle > 360.0f)
	{
		return angle - 360.0f;
	}
	else if(angle < 0.0f)
	{
		return angle + 360.0f;
	}
	else
	{
		return angle;
	}
}

float QqhToRGB(float q1, float q2, float hue)
{
	hue = wrapdegree(hue);
	if(hue < 60.0f)
	{
		return q1 + (q2 - q1) * hue / 60.0f;
	}
	else if(hue < 180.0f)
	{
		return q2;
	}
	else if(hue < 240.0f)
	{
		return q1 + (q2 - q1) * (240.0f - hue) / 60.0f;
	}
	else
	{
		return q1;
	}
}

RGBAf HLSAf(float hue, float light, float sat, float a)
{
	float p1, p2;
	if(light <= 0.5f)	{ p2 = light * (1.0f + sat); } else { p2 = light + sat - light * sat; }
	p1 = 2.0f * light - p2;
	if(sat == 0.0f)
	{
		return RGBAf(light, light, light, a);
	}
	else
	{
		return RGBAf(QqhToRGB(p1, p2, hue + 120.0f), QqhToRGB(p1, p2, hue), QqhToRGB(p1, p2, hue - 120.0f), a);
	}
}

void coolves_test_basicobjects()
{
	using namespace cvMath;

	/// Alpha Cool VES bootstrap!
	ObjectSubSystem * objsys = ObjectSubSystem::getInstance();
	RenderingDevice * rensys = RenderingDevice::getInstance();
	cvRenderInterface::BatchObject<cvRenderInterface::Line> *linebatch, *camera_batch;
	cvRenderInterface::Line * lines[6], *camera_borders[6];

	EmptyObject * objects[6] = {0};

	rensys->OpenScreen(800,600);

	linebatch = rensys->getRenderer()->lineFactory(true);		// allocate batch
	linebatch->map(lines, 6);									// map primitives.
	camera_batch = rensys->getRenderer()->lineFactory(true);
	camera_batch->map(camera_borders, 4);

	// 'muotoillaan' primitiivit:
	camera_borders[0]->verts(cvVert(-390.0f, 290.0f), cvVert(390.0f, 290.0f));
	camera_borders[1]->verts(cvVert(390.0f, 290.0f), cvVert(390.0f, -290.0f));
	camera_borders[2]->verts(cvVert(390.0f, -290.0f), cvVert(-390.0f, -290.0f));
	camera_borders[3]->verts(cvVert(-390.0f, -290.0f), cvVert(-390.0f, 290.0f));

	// set color to the lines..
	for(size_t i = 0; i < 6; ++i)
	{
		lines[i]->colorv1(RGBAf(1.0f,0.0f,0.0f, 1.0f));
		lines[i]->colorv2(RGBAf(0.0f,0.0f,1.0f, 0.6f));

		objects[i] = objsys->create<EmptyObject>();
	}

	objects[4]->setParent(objects[0]);
	objects[3]->setParent(objects[0]);
	objects[2]->setParent(objects[3]);
	objects[1]->setParent(objects[4]);

	objects[5]->setParent(objects[1]);
	objects[1]->setRelativity(OBJ_XY);	// absolute rotation.

	objects[0]->turn(90.0f);
	objects[4]->turn(20.0f);
	objects[2]->turn(-20.0f);
	objects[5]->turn(15.0f);
	objects[4]->move(50.0f);
	objects[3]->move(50.0f);
	objects[2]->move(50.0f);
	objects[1]->move(50.0f);
	objects[5]->move(50.0f);


	/// WE NEED THE CAMERA OBJECTS!! (coming soon! :D )
	Mat4x4f camera_modelview, camera_projmat, tempmat;
	Vec2f camera_pos(0.0, 0.0);
	Vec2f camera_offset(0.0, 0.0);	// coodinates are in percent's.
	float camera_angle = 0.0f;
	float camera_zoom = 1.0f;

	int w, h, x, y;
	glfwGetWindowSize(&w, &h);

	const Vec2f camera_orgin(w/2.0f, h/2.0f);
	bool running = true;
	while(running)
	{
		glfwGetWindowSize(&w, &h);
		glfwGetMousePos(&x, &y);

		camera_zoom += (glfwGetKey(GLFW_KEY_KP_ADD) - glfwGetKey(GLFW_KEY_KP_SUBTRACT)) * 0.0001f;
		camera_offset.x = (x - w/2.0) / ((float)w/2.0f);
		camera_offset.y = -(y - h/2.0) / ((float)h/2.0f);
		camera_angle += (glfwGetKey(GLFW_KEY_KP_4) - glfwGetKey(GLFW_KEY_KP_6)) * 0.01f;
		camera_pos.x += (glfwGetKey(GLFW_KEY_KP_3) - glfwGetKey(GLFW_KEY_KP_1)) * 0.1f;
		camera_pos.y += (glfwGetKey(GLFW_KEY_KP_2) - glfwGetKey(GLFW_KEY_KP_5)) * 0.1f;

		// "WORLD COORDINATE MODE"
		//renderer->setCamera(0);
		//{
		// SETUP VIEWPORT
		glViewport(0, 0, w, h);

		// SETUP PROJECTION MATRIX
		identity(camera_projmat);	// set identity
		camera_projmat *= ortho_world2d((float)w, (float)h);	// apply world coordinate projection

		// SETUP MODELVIEW MATRIX - todo, too mucho 3d effect? A BIT?
		identity(camera_modelview);											// set identity
		translationXY(camera_modelview, camera_offset * -camera_orgin);		// insert transation
		camera_modelview *= scaleXY(camera_zoom, camera_zoom);				// multiply by scaling matrix.
		// multiply by translation-rotation matrix.
		camera_modelview *= rotationZ(translationXY(identity(tempmat), camera_pos+(camera_offset * camera_orgin)), camera_angle);

		// LOAD MATRIXES
		glMatrixMode(GL_PROJECTION);
		glLoadMatrixf((GLfloat*)&camera_projmat.mat);
		glMatrixMode(GL_MODELVIEW);
		glLoadMatrixf((GLfloat*)&camera_modelview.mat);
		//}

		if(glfwGetKey(GLFW_KEY_LEFT))
		{
			objects[0]->turn(0.1f);	// rotate whole thing
		}
		else if(glfwGetKey(GLFW_KEY_RIGHT))
		{
			objects[0]->turn(-0.1f);	// rotate whole thing
		}


		/// WRITE VERTEXES:
		lines[0]->verts(objects[4]->position(), objects[0]->position());
		lines[1]->verts(objects[3]->position(), objects[0]->position());
		lines[2]->verts(objects[2]->position(), objects[3]->position());
		lines[3]->verts(objects[1]->position(), objects[4]->position());
		lines[3]->verts(objects[1]->position(), objects[5]->position());
		lines[5]->verts(objects[4]->position(), objects[1]->position());
		lines[4]->verts(objects[0]->position(), cvVert(0.0f, 0.0f));	// world orgin..


		for(size_t i = 0; i < 240; i++)
		{
			rensys->getRenderer()->lineFactory()->mapSingleshot()
			->verts(cvVert(-360.0f + i, 250.0f), cvVert(-360.0f + i-5.0f, 230.0f))
			->color(HLSAf((float)i, 0.5, 0.8, 0.9));
		}

		objsys->updateObjects();
		rensys->getRenderer()->render();
		rensys->flip();


		if(glGetError() != GL_NO_ERROR)
		{
			throw new Exception("got GL ERRORS!");
		}
		if(glfwGetKey(GLFW_KEY_ESC) || glfwGetWindowParam(GLFW_OPENED) == GL_FALSE)	{ running = false; }

	}	// while(running)...


	//objsys->destroyAllObjects();
}
